'''
Created on Jun 7, 2013

@author: Petko
'''
from utils.sententialyzer_client import SententialyzerClient
import unittest
from opinion import checkAttentions, check_opinion_tree, ClientThread
import time
import json

class UncommittedStateTest(unittest.TestCase):
    sc = SententialyzerClient()
    
    def setUp(self):
        delete_opinions=True
        if delete_opinions:
            self.sc.authorize('moderator1', 'test', 'bopin_r bopin_d')
            result = self.sc.viewListOp('all_base_opinions', 'newest', access_token = self.sc.accessToken)
            page = result['page']
            for op in page:
                self.sc.deleteBaseOp(op['id'])
            
        self.sc.commenterId = 1
        
    def authAndCreateOp(self, parent, evaluation, **kwargs):
        return self.sc.authAndCreateOp(None, parent, evaluation, **kwargs)
    
    def test_several_creations_per_commit(self):
        self.sc.authorize('moderator1', 'test', 'bopin_c')
        
        result = self.sc.createBaseOp("test_several_creations_per_commit", "test_several_creations_per_commit", "moderators:d sent_default_group:rc")
        
        baseOpId = result['id']
        
        op1 = self.authAndCreateOp(baseOpId, 'positive', title = 'op1+', content = 'op plus', commit_pending_ops=False)
        
        access_token_backup = self.sc.accessToken
        
        #still nothing
        checkAttentions(self, baseOpId, 0, 0, 0)
        
        self.authAndCreateOp(baseOpId, 'positive', title = 'op2+', content = 'op plus', commit_pending_ops=False)
        
        checkAttentions(self, baseOpId, 0, 0, 0)
        
        self.sc.commit_pending_ops()
        
        check_opinion_tree(self, baseOpId, 2)
        
        checkAttentions(self, baseOpId, 2, 0, 2)
        
        self.authAndCreateOp(baseOpId, 'negative', title = 'op3-', content = 'op plus', commit_pending_ops=False)
        self.authAndCreateOp(baseOpId, 'negative', title = 'op4-', content = 'op plus', commit_pending_ops=False)
        
        self.sc.accessToken = access_token_backup
        #like your own opinion
        self.sc.createOp(op1['id'], "positive", commit_pending_ops=False)
        
        checkAttentions(self, baseOpId, 2, 0, 2)
        
        self.sc.commit_pending_ops()
        
        check_opinion_tree(self, baseOpId, 4)
        
        checkAttentions(self, baseOpId, 1, 2, 4)
        checkAttentions(self, op1['id'], 1, 0, 0)
        
    def test_non_duplicate_vote(self):
        self.sc.authorize('moderator1', 'test', 'bopin_c')
        
        result = self.sc.createBaseOp("test_non_duplicate_vote", "test_non_duplicate_vote", "moderators:d sent_default_group:rc")
        
        baseOpId = result['id']
        
        op1 = self.authAndCreateOp(baseOpId, 'positive', title = 'op1+', content = 'op plus', commit_pending_ops=True)
        
        self.authAndCreateOp(op1['id'], 'positive', title = 'op1+', content = 'op plus', commit_pending_ops=False)
        
        #access_token_backup = self.sc.accessToken
         
        self.sc.createOp(baseOpId, "positive", commit_pending_ops=False)
        
        op2 = self.sc.createOp(baseOpId, "positive", title = 'op2+', content = 'op plus', commit_pending_ops=False)
        
        self.sc.commit_pending_ops()
        
        checkAttentions(self, baseOpId, 2, 0, 3)
        
        self.sc.createOp(op1['id'], "positive", commit_pending_ops=False)
        
        self.sc.updateOp(op2['id'], evaluation='negative', commit_pending_ops=False)
        
        self.sc.commit_pending_ops()
        
        checkAttentions(self, baseOpId, 1, 1, 3)
        
        op3 = self.sc.createOp(baseOpId, "positive", title='test', commit_pending_ops=False)
        
        self.sc.updateOp(op2['id'], evaluation='negative', commit_pending_ops=False)
        
        self.sc.updateOp(op3['id'], evaluation='positive', commit_pending_ops=False)
        
        self.sc.commit_pending_ops()
        
        checkAttentions(self, baseOpId, 2, 0, 4)
    
        op4 = self.sc.createOp(op1['id'], "neutral", title='test', commit_pending_ops=False)
        
        self.sc.updateOp(op4['id'], evaluation='negative', commit_pending_ops=False)
        
        self.sc.commit_pending_ops()
        
        checkAttentions(self, baseOpId, 1, 1, 5)
    
    def test_create_and_delete(self):
        self.sc.authorize('moderator1', 'test', 'bopin_c')
        
        result = self.sc.createBaseOp("test_create_and_delete", "test_create_and_delete", "moderators:d sent_default_group:rc")
        
        baseOpId = result['id']
        
        op = self.authAndCreateOp(baseOpId, 'positive', title = 'op1+', content = 'op plus', commit_pending_ops=False)
        
        self.sc.deleteOp(op['id'], commit_pending_ops=True)
        
        checkAttentions(self, baseOpId, 0, 0, 0)
#    def test_modify_not_yet_inserted(self):
#        self.sc.authorize('moderator1', 'test', 'bopin_c')
#        
#        result = self.sc.createBaseOp("test_non_duplicate_vote", "test_non_duplicate_vote", "moderators:d sent_default_group:rc")
#        
#        baseOpId = result['id']
#    
#        self.authAndCreateOp(baseOpId, 'positive', title = 'op1+', commit_pending_ops=False)
#        
#        #now create another positive while the non-neutral is still not committed
#        self.sc.createOp(baseOpId, "positive", commit_pending_ops=False)
#        
    def test_2_deletes(self):
        self.sc.authorize('moderator1', 'test', 'bopin_c')
        
        result = self.sc.createBaseOp("base op", "base op", "moderators:d sent_default_group:rc")
        baseOpId = result['id']
        
        op1 = self.authAndCreateOp(baseOpId, 'positive', title = 'op1+', content = 'op plus', commit_pending_ops=True)
        
        self.authAndCreateOp(baseOpId, 'neutral', title = 'op=', commit_pending_ops=True)
        
        op2 = self.authAndCreateOp(baseOpId, 'negative', title = 'op2-', commit_pending_ops=True)
        
        op11 = self.authAndCreateOp(op1['id'], 'positive', title = 'op11+', commit_pending_ops=False)
        
        op21 = self.sc.createOp(op2['id'], 'positive', title = 'op21+', commit_pending_ops=True)
        
        check_opinion_tree(self, baseOpId, 5)
        
        self.sc.deleteOp(op11['id'], commit_pending_ops=False)
        self.sc.deleteOp(op21['id'], commit_pending_ops=True)
        
        check_opinion_tree(self, baseOpId, 3)
        

            
    class CommitThread(ClientThread):
        def __init__(self, create_id):
            ClientThread.__init__(self, create_id)
            
        def operation(self):
            self.async_sc.commit_pending_ops(commit_delay=self.commit_delay)
                
    def test_create_synchronization(self):
        self.sc.authorize('moderator1', 'test', 'bopin_c')
        
        result = self.sc.createBaseOp("test_non_duplicate_vote", "test_non_duplicate_vote", "moderators:d sent_default_group:rc")
        
        baseOpId = result['id']
        
        op1 = self.authAndCreateOp(baseOpId, 'positive', title = 'op1+', content = 'op plus')
        
        #self.authorize('commenter_sync', 'emptypass', 'all:rc')
        
        class CreateThread(ClientThread):
            def __init__(self, create_id):
                ClientThread.__init__(self, create_id)
                self.commit_delay=2
                
            def operation(self):
                self.async_sc.createOp(op1['id'], 'positive', commit_pending_ops=False, commit_delay=self.commit_delay)
                #self.async_sc.createOp(baseOpId, 'positive', commit_pending_ops=False, commit_delay=2)
                
        
                
        t1 = CreateThread(1)
        t2 = CreateThread(2)
        
        t1.start()
        t2.start()
        
        t1.join()
        t2.join()
        
        #non of the creates waits for the other
        self.assertLess(t1.execution_time, 2.8)
        self.assertLess(t2.execution_time, 2.8)
        
        self.sc.commit_pending_ops()
        
        t1 = CreateThread(1)
        t3 = UncommittedStateTest.CommitThread(3)
        
        #create an opinion to add a pending operation
        self.authAndCreateOp(op1['id'], 'positive', title = 'op2+', commit_pending_ops=False)
        
        #start commit while creating
        t1.start()
        #sleep a little to let the create query start
        time.sleep(0.1)
        t3.start()
        
        t1.join()
        t3.join()
        
        self.assertGreater(t3.execution_time, 2.1)
        
        t1 = CreateThread(4)
        t3 = UncommittedStateTest.CommitThread(5)
        
        #create an opinion to add a pending operation
        self.authAndCreateOp(op1['id'], 'positive', title = 'op2+', commit_pending_ops=False)
        
        #start create while committing. The create must wait
        t3.commit_delay=2
        t3.start()
        time.sleep(0.1)
        
        t1.start()
        
        t1.join()
        t3.join()
        
        self.assertGreater(t1.execution_time, 2)
        
        commit_result = self.sc.commit_pending_ops()
        print json.dumps(commit_result)
        self.assertNotEqual(len(commit_result), 0) 
    
    def test_update_create_synchronization(self):
        self.sc.authorize('moderator1', 'test', 'bopin_c')
        
        result = self.sc.createBaseOp("test_non_duplicate_vote", "test_non_duplicate_vote", "moderators:d sent_default_group:rc")
        
        baseOpId = result['id']
        
        #op1 = self.authAndCreateOp(baseOpId, 'positive', title = 'op1+', content = 'op plus')
        
        #self.authorize('commenter_sync', 'emptypass', 'all:rc')
        op1 = None
        class CreateThread(ClientThread):
            def __init__(self, create_id):
                ClientThread.__init__(self, create_id)
                self.commit_delay=2
                
            def operation(self):
                self.async_sc.createOp(op1['id'], 'positive', commit_pending_ops=False, commit_delay=self.commit_delay)
        
        class UpdateThread(ClientThread):
            def __init__(self, create_id):
                ClientThread.__init__(self, create_id)
                self.commit_delay=2
                
            def operation(self):
                self.async_sc.updateOp(op1['id'], evaluation = 'negative', commit_pending_ops=False, commit_delay=self.commit_delay)
        
        tc = CreateThread(1)
        tu = UpdateThread(1)
        
        op1 = tc.async_sc.createOp(baseOpId, 'neutral', title = 'op1+', content = 'op plus')
        
        checkAttentions(self, baseOpId, 0, 0, 1)
        
        #set the opinion as negative while creating another non-negative opinion
        tc.start()
        time.sleep(0.1)
        
        tu.start()
        
        tc.join()
        tu.join()
        
        self.sc.commit_pending_ops()
        
        #the create opinion should be deleted immediately
        checkAttentions(self, baseOpId, 0, 1, 1)
        
        tc = CreateThread(2)
        tu = UpdateThread(2)
        tp = UncommittedStateTest.CommitThread(2)
        
        tp.commit_delay=2
        tu.commit_delay=0
        
        op1 = tc.async_sc.createOp(baseOpId, 'positive', title = 'op2+', commit_pending_ops=False)
        
        tp.start()
        time.sleep(0.1)
        
        tu.start()
        
        tp.join()
        tu.join()
        
        #the update should be waiting the commit
        self.assertGreater(tu.execution_time, 2)
        
        #the update should not be committed
        checkAttentions(self, baseOpId, 1, 1, 2)
        
        self.sc.commit_pending_ops()
        
        #the update should NOW be committed
        checkAttentions(self, baseOpId, 0, 2, 2)
        
        #make it positive for the next test
        tu.async_sc.updateOp(op1['id'], evaluation = 'positive', commit_pending_ops=True)
        
        #create another opinion so the commit has what to choke
        tc.async_sc.createOp(baseOpId, 'positive', title = 'op=', commit_pending_ops=False)
        
        tu = UpdateThread(2)
        tp = UncommittedStateTest.CommitThread(2)
        
        tp.commit_delay=0
        tu.commit_delay=2
        
        #the commit should be waiting the update
        tu.start()
        time.sleep(0.1)
        
        tp.start()
        
        tp.join()
        tu.join()
        
        self.assertGreater(tp.execution_time, 2)
        
        checkAttentions(self, baseOpId, 0, 2, 3)
    
    def test_insert_in_deleted(self):
        self.sc.authorize('moderator1', 'test', 'bopin_c')
        
        result = self.sc.createBaseOp("test_non_duplicate_vote", "test_non_duplicate_vote", "moderators:d sent_default_group:rc")
        
        baseOpId = result['id']
        
        op1 = self.authAndCreateOp(baseOpId, 'positive', title = 'op1+', content = 'op plus')
        
        class CreateThread(ClientThread):
            def __init__(self, create_id):
                ClientThread.__init__(self, create_id)
                self.commit_delay=2
                
            def operation(self):
                self.async_sc.createOp(op1['id'], 'positive', title='aaa', commit_pending_ops=False, commit_delay=self.commit_delay)
                
        class DeleteThread(ClientThread):
            def __init__(self, create_id):
                ClientThread.__init__(self, create_id)
                self.commit_delay=2
                
            def operation(self):
                self.async_sc.deleteOp(op1['id'], commit_pending_ops=False, commit_delay=self.commit_delay)
                
        tc = CreateThread(1)
        td = DeleteThread(1)
        
        #set the access token that created op1
        td.async_sc.accessToken = self.sc.accessToken
        
        tc.start()
        time.sleep(0.1)
        
        td.start()
        
        tc.join()
        td.join()
        
        self.sc.commit_pending_ops()
        
        checkAttentions(self, baseOpId, 0, 0, 2)
        
if __name__ == '__main__':
    
    unittest.main()